package com.wonder.fileSpit;

import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.concurrent.*;

/**
 * 异步写文件
 * 调用init方法初始化线程参数，参数1：结束标记，参数2：文件路径，参数3：缓存队列容量
 * add()，往文本加数据，线程会自动将数据写入文件
 * add()，添加结束标记，结束线程，文件写完
 *
 * @author: jinrong.wang
 * @date: 2018/5/15 14:09
 */
public class FileWriteCallable implements Callable {
    /**
     * 缓存队列，缓存数据线程会将数据写入文件
     */
    private LinkedBlockingDeque<String> dataQueue;
    /**
     * 文件路径,/tmp/0.test.txt
     */
    private String filePath;
    /**
     * 结束标记位：FILE_WRITE_OVER
     */
    private String endFlag;
    /**
     * 线程优雅关停标记
     */
    private boolean flag = true;

    /**
     * @param endFlag  结束标记位，当缓存队列中收到此标记，则将缓存数据写入文件，关停线程
     * @param filePath 文件路径
     * @param capacity 缓存队列容量
     */
    public void init(String endFlag, String filePath, int capacity) {
        this.endFlag = endFlag;
        this.filePath = filePath;
        dataQueue = new LinkedBlockingDeque(capacity);
        System.out.println("初始化线程信息");
    }

    /**
     * 线程执行方法
     *
     * @return
     * @throws Exception
     */
    @Override
    public Object call() {
        try (PrintWriter writer = new PrintWriter(new FileWriter(filePath))) {
            int count = 0;
            while (flag) {
                try {
                    //缓存队列中获取数据,队列没有数据，则睡眠1秒
                    String line = dataQueue.poll(1, TimeUnit.SECONDS);
                    //收到文件内容结束标志，则将缓存刷入磁盘，结束线程
                    if (line != null && line.equals(endFlag)) {
                        //文件流自带缓存，收到文件结束标志，手动把缓存数据写入磁盘
                        writer.flush();
                        //关闭while循环，结束线程
                        flag = false;
                        System.out.println("写入文本行数count=" + count+",filePath="+filePath);
                    } else if (line != null && !"".equals(line)) {
                        //缓存数据写入文件,实际是写入文件流，操作系统不会立刻写入文件
                        writer.println(line);
                        count++;
                    }
                } catch (Exception e) {
                    System.out.println("文件写入异常1，关闭线程，filePath="+filePath);
                    e.printStackTrace();
                    flag = false;
                    return false;
                }
            }

        } catch (Exception e) {
            System.out.println("文件写入异常2，filePath="+filePath);
            return false;
        } finally {
            System.out.println("文件写完，线程结束，filePath="+filePath);
        }

        return true;
    }

    /**
     * 添加数据,线程会自动将数据写入文件
     * @param line
     */
    public void add(String line) {
        try {
            dataQueue.put(line);
        } catch (Exception e) {
            System.out.println("队列写入异常");
        }
    }


    public static void main(String[] args) {
        FileWriteCallable fileWriteCallable = new FileWriteCallable();
        fileWriteCallable.init("FILE_WRITE_OVER", "D:\\temp\\0.test.txt", 1000);
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        //启动线程
        Future<Boolean> future = executorService.submit(fileWriteCallable);
        System.out.println("提交线程，线程启动自动调用call()方法");
        for (int i = 0; i < 100; i++) {
            fileWriteCallable.add("第" + i + "行数据");
        }
        //数据写完了，添加结束标记endFlag参数值，线程将自动结束
        fileWriteCallable.add("FILE_WRITE_OVER");

        try {
            //future.get()会阻塞main线程，直到线程执行完毕。
            boolean result = future.get();
            System.out.println("线程执行完毕，result=" + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
